热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

音轨|制定者_Android音频源码分析——AudioFlinger

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Android音频源码分析——AudioFlinger相关的知识,希望对你有一定的参考价值。Android音频源码分

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Android 音频源码分析——AudioFlinger相关的知识,希望对你有一定的参考价值。


Android 音频源码分析——AndroidRecord录音(一)
Android 音频源码分析——AndroidRecord录音(二)
Android 音频源码分析——AndroidRecord音频数据传输流程
Android 音频源码分析——audioserver启动
Android 音频源码分析——AudioFlinger
Android 音频源码分析——AudioTrack设备选择

源码基于Android9.0

混音:把多种来源的声音,整合至一个立体音轨或单音轨中。

AudioFlinger和AudioPolicyService是音频中的两大服务,这里先整理下AudioFlinger。

AudioPolicyService时策略制定者,不和AudioHal直接交互;而AudioFlinger是策略执行者,与AudioHal层进行交互。


简介

AudioFlinger运行在audioserver进程中,是一个音频服务。主要职责:


  1. 音频数据接收和转发,录音时从HAL层获取音频数据,转给客户端;播放时从客户端获取数据,并发送给HAL层。
  2. 音频数据处理,包括数据格式转换、声道数转换、重采样、混音、音效、变速、音量等。
  3. 执行AudioFlinger的策略。

与HAL层通信

framework层hidl相关代码目录: frameworks/av/media/libaudiohal

hal层hidl相关代码: hardware/interfaces/audio

 AudioFlinger通过hidl接口的方式调用hal层服务。有上一篇文章可知,启动AudioPolicyService时,会加载音频配置文件,然后根据配置加载HAL module。后续可以调用hal接口进行其他交互(设备选择、通路、读写数据等)。

AudioPolicyService并不直接于Hal层交互,而是由AudioFlinger和Hal通信,这里看看AudioFlinger加载音频模块。


1 加载Module

audio_module_handle_t AudioFlinger::loadHwModule(const char *name)

if (name == NULL)
return AUDIO_MODULE_HANDLE_NONE;

if (!settingsAllowed())
return AUDIO_MODULE_HANDLE_NONE;

Mutex::Autolock _l(mLock);
return loadHwModule_l(name);

// loadHwModule_l() must be called with AudioFlinger::mLock held
audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)

sp<DeviceHalInterface> dev;
int rc &#61; mDevicesFactoryHal->openDevice(name, &dev);
//......
audio_module_handle_t handle &#61; (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE);
mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
return handle;

加载hal层module,mAudioHwDevs保存handle 和AudioHwDevice.

接着看mDevicesFactoryHal->openDevice流程

mDevicesFactoryHal->openDevice(name, &dev);
>>DevicesFactoryHalHybrid::openDevice
>>
status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device)
if (mDevicesFactory &#61;&#61; 0) return NO_INIT;
IDevicesFactory::Device hidlDevice;
status_t status &#61; nameFromHal(name, &hidlDevice);
if (status !&#61; OK) return status;
Result retval &#61; Result::NOT_INITIALIZED;
Return<void> ret &#61; mDevicesFactory->openDevice(
hidlDevice,
[&](Result r, const sp<IDevice>& result)
retval &#61; r;
if (retval &#61;&#61; Result::OK)
*device &#61; new DeviceHalHidl(result);

);
if (ret.isOk())
if (retval &#61;&#61; Result::OK) return OK;
else if (retval &#61;&#61; Result::INVALID_ARGUMENTS) return BAD_VALUE;
else return NO_INIT;

return FAILED_TRANSACTION;


  1. nameFromHal通过name获取Device类型&#xff1a;IDevicesFactory::Device::PRIMARY、IDevicesFactory::Device::A2DP、IDevicesFactory::Device::USB、IDevicesFactory::Device::R_SUBMIX等&#xff1b;
  2. mDevicesFactory->openDevice通过hidl跨进程调用接口

hardware/interfaces/audio/core/all-versions/default/include/core/all-version/DevicesFactory.impl.h

Return<void> DevicesFactory::openDevice(IDevicesFactory::Device device, openDevice_cb _hidl_cb)
switch (device)
case IDevicesFactory::Device::PRIMARY:
return openDevice<PrimaryDevice>(AUDIO_HARDWARE_MODULE_ID_PRIMARY, _hidl_cb);
case IDevicesFactory::Device::A2DP:
return openDevice(AUDIO_HARDWARE_MODULE_ID_A2DP, _hidl_cb);
case IDevicesFactory::Device::USB:
return openDevice(AUDIO_HARDWARE_MODULE_ID_USB, _hidl_cb);
case IDevicesFactory::Device::R_SUBMIX:
return openDevice(AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX, _hidl_cb);
case IDevicesFactory::Device::STUB:
return openDevice(AUDIO_HARDWARE_MODULE_ID_STUB, _hidl_cb);

_hidl_cb(Result::INVALID_ARGUMENTS, nullptr);
return Void();

int DevicesFactory::loadAudioInterface(const char* if_name, audio_hw_device_t** dev)
const hw_module_t* mod;
int rc;
rc &#61; hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
if (rc)
ALOGE("%s couldn&#39;t load audio hw module %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID,
if_name, strerror(-rc));
goto out;

rc &#61; audio_hw_device_open(mod, dev);
if (rc)
ALOGE("%s couldn&#39;t open audio hw device in %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID,
if_name, strerror(-rc));
goto out;

if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN)
ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version);
rc &#61; -EINVAL;
audio_hw_device_close(*dev);
goto out;

return OK;
out:
*dev &#61; NULL;
return rc;


  1. hw_get_module_by_class动态链接 hal 库&#xff0c;如&#xff1a;audio.primary.default.so、audio.a2dp.default.so、audio.usb.default.so
  2. audio_hw_device_open对应module method->open函数

2 open stream

这里看看加载后的openOutput流程

status_t AudioFlinger::openOutput(audio_module_handle_t module,
audio_io_handle_t *output,
audio_config_t *config,
audio_devices_t *devices,
const String8& address,
uint32_t *latencyMs,
audio_output_flags_t flags)

if (devices &#61;&#61; NULL || *devices &#61;&#61; AUDIO_DEVICE_NONE)
return BAD_VALUE;

Mutex::Autolock _l(mLock);
sp<ThreadBase> thread &#61; openOutput_l(module, output, config, *devices, address, flags);
//......
return NO_INIT;

sp<AudioFlinger::ThreadBase> AudioFlinger::openOutput_l(audio_module_handle_t module,
audio_io_handle_t *output,
audio_config_t *config,
audio_devices_t devices,
const String8& address,
audio_output_flags_t flags)

AudioHwDevice *outHwDev &#61; findSuitableHwDev_l(module, devices);
//......
AudioStreamOut *outputStream &#61; NULL;
status_t status &#61; outHwDev->openOutputStream(
&outputStream,
*output,
devices,
flags,
config,
address.string());
mHardwareStatus &#61; AUDIO_HW_IDLE;
if (status &#61;&#61; NO_ERROR)
if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)
sp<MmapPlaybackThread> thread &#61;
new MmapPlaybackThread(this, *output, outHwDev, outputStream,
devices, AUDIO_DEVICE_NONE, mSystemReady);
mMmapThreads.add(*output, thread);
return thread;
else
sp<PlaybackThread> thread;
if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
thread &#61; new OffloadThread(this, outputStream, *output, devices, mSystemReady);
else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)
|| !isValidPcmSinkFormat(config->format)
|| !isValidPcmSinkChannelMask(config->channel_mask))
thread &#61; new DirectOutputThread(this, outputStream, *output, devices, mSystemReady);
else
thread &#61; new MixerThread(this, outputStream, *output, devices, mSystemReady);

mPlaybackThreads.add(*output, thread);
return thread;


return 0;


  1. findSuitableHwDev_l找寻对应的AudioHwDevice&#xff1b;
  2. openOutputStream打开输出Stream&#xff0c;这里会调用到hal层 adev_open_output_stream中&#xff0c;这里就不分析了&#xff1b;
  3. 根据flags创建不同的通路线程&#xff1b;
  4. 将线程添加到mPlaybackThreads&#xff1b;

openinput 流程就先不分析了。


与APP通信

App和AudioFlinger间音频数据则通过共享内存的方式进行传输。

如下图AudioRecord的数据传输&#xff1a;

如下图AudioTrack的数据传输&#xff1a;

AudioFlinger是一个binder服务&#xff0c;app中的native层可以通过获取其binder服务从而进行交互。

如下示例&#xff1a;

const sp<IAudioFlinger>& audioFlinger &#61; AudioSystem::get_audio_flinger();
record &#61; audioFlinger->createRecord(input,
output,
&status);

推荐阅读
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
author-avatar
shzq110_113
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有